﻿using System.Threading;

namespace XYCAM.Generator.CAD;

/// <summary>
/// 命令对象生成器,读取标记了XyCommandObject特性的类,生成一个CommandManager类型,里面包含一个List集合的 CommandObjects属性
/// </summary>
[Generator(LanguageNames.CSharp)]
public sealed class XyCommandObjectAttributeGenerator : IIncrementalGenerator
{
    /// <summary>
    /// 类特性
    /// </summary>
    private const string ClassAttrbuteName = "XyCommandObject";

    /// <summary>
    /// 被识别的类名
    /// </summary>
    private const string AllowBaseClassName = ConstStrings.AllowBaseClassName;

    /// <summary>
    /// 生成文件的后缀名
    /// </summary>
    private const string Suffix = ConstStrings.Suffix;


    /// <summary>
    /// 实例中的命令集合私有字段
    /// </summary>
    private const string CommandsField = "m_CommandObjects";

    /// <summary>
    /// 实例中的命令集合私有字段
    /// </summary>
    private const string CommandsProperty = "CommandObjects";


    /// <summary>
    /// 命令管理器的类名
    /// </summary>
    private const string ManagerClass = "CommandManager";

    /// <summary>
    /// 初始化操作
    /// </summary>
    /// <param name="context"></param>
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        ////启动调试器
        //if (!Debugger.IsAttached)
        //{
        //    Debugger.Launch();
        //}

        // 注册源代码输出，将在编译期间生成并输出源代码
        context.RegisterSourceOutput(
            context.SyntaxProvider.ForAttributeWithMetadataName(
                $"{ConstStrings.AttrbutesNameSpace}.{ClassAttrbuteName}Attribute",
                //前置判断
                Predicate,
                 //数据转换
                 Transform
                ).Collect(),
            //生成代码
            Create);
    }


    /// <summary>
    /// 转换未私有字段字符
    /// </summary>
    /// <param name="typeName"></param>
    /// <param name="suffix"></param>
    /// <returns></returns>
    private static string ToFieldString(string typeName, string suffix)
    {
        string fristStr = typeName[0].ToString().ToLower();
        if (typeName.Length < 2) return $"m_{fristStr}{suffix}";
        var name = typeName.Substring(1, typeName.Length - 1);
        //生成字段的名称
        var field = $"m_{fristStr}{name}{suffix}";
        return field;
    }


    private void Create(SourceProductionContext context, ImmutableArray<ClassGatheredData?> array)
    {
        string projectName = "";

        // 获取命名空间字符串
        StringBuilder propertysBuilder = new StringBuilder();
        // 遍历收集到的数据
        foreach (var classGatheredData in array)
        {
            if (classGatheredData is null) continue;
            var @namespace = classGatheredData.Namespace;

            if (projectName == "")
            {
                //获取项目名
                var dotIndex = @namespace.IndexOf(".");
                projectName = @namespace;
                if (dotIndex > 0)
                {
                    projectName = projectName.Substring(0, dotIndex);
                }
            }


            var typeName = classGatheredData.TypeName;

            //生成字段的名称
            var field = ToFieldString(typeName, "CommandObject");

            var globalTypeName = $"{@namespace}.{typeName}";

            //往方法中添加字符
            string addObjectsString = $$"""
									{{globalTypeName}} {{field}} = new {{globalTypeName}}();
									{{CommandsField}}.Add({{field}});
					""";
            propertysBuilder.AppendLine(addObjectsString);
        }

        if (projectName == "") return;

        var S = "global::";
        projectName = projectName.TrimStart([.. S]);

        string listTypeString = "System.Collections.Generic.List";

        var sourceString = $$"""
				// <auto-generated/>
				
				#nullable enable
				namespace {{projectName}}.{{ManagerClass}};
				
				public static class {{projectName}}_{{ManagerClass}}
				{
					private static {{listTypeString}}<object>? {{CommandsField}};
					
					/// <summary>
					/// 所有标记了{{ClassAttrbuteName}}特性的命令类实例
					/// <summary>
					public static {{listTypeString}}<object> {{CommandsProperty}}
					{
						get
						{
							if ({{CommandsField}} is null)
							{
								{{CommandsField}} = new {{listTypeString}}<object>();
				{{propertysBuilder}}
							}
							return {{CommandsField}};
						}
					}
				}
				""";

        // 添加生成的源代码
        context.AddSource($"{ManagerClass}{Suffix}", sourceString);
    }


    private ClassGatheredData? Transform(GeneratorAttributeSyntaxContext context, CancellationToken token)
    {
        //语义节点必须为方法
        if (context.TargetNode is not ClassDeclarationSyntax)
        {
            return null;
        }

        //语义节点必须为方法
        if (context.TargetSymbol is not ITypeSymbol
            {
                //获取方法名
                Name: var typeName,
                // 并且 gasc 的 ContainingType 属性的 Name 属性为 var typeName
                ContainingNamespace: var @namespace,
            })
        {
            return null;
        }

        return new ClassGatheredData(typeName, @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
    }




    /// <summary>
    /// 断言类型节点
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    static bool Predicate(SyntaxNode node, CancellationToken _)
    {
        //判断是否为类型语义节点
        if (node is not ClassDeclarationSyntax
            {
                //父语义节点的访问修饰符不能为空
                //Modifiers: var modifiers and not [],
                Modifiers: SyntaxTokenList
                {
                    Count: > 0,
                } modifiers,
                //获取基类列表,并且不能为空,就是说必须为派生类,所以它也不会为静态类
                BaseList.Types: SeparatedSyntaxList<BaseTypeSyntax>
                {
                    Count: > 0,
                } baseTypes,
            })
        {
            return false;
        }

        //类型的修饰符必须包含Partial
        if (!modifiers.Any(SyntaxKind.PartialKeyword))
        {
            return false;
        }
        //获取基类的名称,第一个才是类型,后面或许还有接口
        var baseTypeName = baseTypes.First().ToString();
        if (GetGenerictypeNameStruct(baseTypeName) is not (var typeName, var generictypeName))
        {
            return false;
        }
        //分割泛型参数,只能有一个参数
        if (generictypeName.Contains(','))
        {
            return false;
        }

        //判断是否与指定类型一致
        return typeName.Equals(AllowBaseClassName);
    }


    /// <summary>
    /// 获取基类的字符和泛型参数字符
    /// </summary>
    /// <param name="baseTypeName"></param>
    /// <returns></returns>
    private static (string TypeName, string GenericTypeNames)? GetGenerictypeNameStruct(string baseTypeName)
    {
        //必须包含泛型参数,但是我不知道怎么获取,目前只能用字符串分割了
        var indexStart = baseTypeName.IndexOf("<");
        if (indexStart < 0) return null;

        var indexEnd = baseTypeName.IndexOf(">");
        if (indexEnd < 0) return null;

        //获取去除了泛型集合的类型名
        //var typeName = baseTypeName[..indexStart];

        var typeName = baseTypeName.Substring(0, indexStart);


        indexStart++;
        //获取泛型参数集合
        //var GenericTypeNames = baseTypeName[indexStart..indexEnd];
        var GenericTypeNames = baseTypeName.Substring(indexStart, indexEnd - indexStart);

        return (typeName, GenericTypeNames);
    }

    /// <summary>
    /// 方法名称
    /// </summary>
    /// <param name="Name">方法名</param>
    sealed record MethodGatheredData(string Name);

    /// <summary>
    /// 类型数据集合
    /// </summary>
    /// <param name="TypeName">类名</param>
    /// <param name="Namespace">命名空间</param>
    sealed record ClassGatheredData(
        string TypeName,
        string Namespace
        );


}



